package com.hmall.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.hmall.common.dto.PageDTO;
import com.hmall.search.pojo.AggregationEnum;
import com.hmall.search.pojo.ItemDoc;
import com.hmall.search.pojo.RequestParams;
import com.hmall.search.service.SearchService;
import lombok.SneakyThrows;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.util.*;

/**
 * @Author Mingxi Guo
 * @Date 2022年7月16日21:14:07
 * @Email guomingxi2018@163.com
 */

@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

     private static final String INDEX_NAME ="mall";

    /**
     * 搜索框自动补全
     *
     * @param prefix
     * @return
     */
    @Override
    @SneakyThrows
    public List<String> suggest(String prefix) {
        //查询
        SearchRequest searchRequest = new SearchRequest(INDEX_NAME);
      searchRequest.source().suggest(new SuggestBuilder()
              .addSuggestion("mallSuggest", SuggestBuilders
                      .completionSuggestion("suggestion")
                      .skipDuplicates(true).prefix(prefix).size(10)));

        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
         //处理查询结果

        Suggest suggest = searchResponse.getSuggest();
       CompletionSuggestion mallSuggest = suggest.getSuggestion("mallSuggest");

        List<CompletionSuggestion.Entry.Option> options = mallSuggest.getOptions();

        ArrayList<String> list = new ArrayList<>(options.size());
         options.forEach(option -> {
             String text = option.getText().toString();
             list.add(text);

         });

        return list;
    }

    /**
     * 输入框搜索
     *
     * @param requestParams
     * @return
     */
    @Override
    @SneakyThrows
    public PageDTO<ItemDoc> list(RequestParams requestParams) {
        SearchRequest searchRequest = new SearchRequest(INDEX_NAME);
        //基础查询,封装函数
        basicSearch(requestParams,searchRequest);

        // 排序
        if (!"default".equals(requestParams.getSortBy())){
            searchRequest.source().sort(requestParams.getSortBy(), SortOrder.DESC);
        }
        //分页
        Integer page = requestParams.getPage();
        Integer size = requestParams.getSize();
        searchRequest.source().from((page-1)*size).size(size);
        //高亮
        searchRequest.source().highlighter(new HighlightBuilder().field("name").requireFieldMatch(false));
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        return handleResponse(searchResponse); //结果处理封装函数
    }



    /**
     * 处理查询结果
     * @param searchResponse
     * @return
     */

    private PageDTO<ItemDoc> handleResponse(SearchResponse searchResponse) {
        SearchHits searchHits = searchResponse.getHits();
        SearchHit[] hits = searchHits.getHits();
        long total = searchHits.getTotalHits().value;
        ArrayList<ItemDoc> itemDocs = new ArrayList<>(hits.length);
        for (SearchHit hit : hits) {
            String json = hit.getSourceAsString();
            ItemDoc itemDoc = JSON.parseObject(json, ItemDoc.class);
             //高亮处理封装函数
            String highLightField = getHighLightField(hit);
            if (highLightField !="" && highLightField !=null){
                itemDoc.setName(highLightField);
            }

            itemDocs.add(itemDoc);


        }
         return new PageDTO<>(total, itemDocs);
    }

    /**
     * 获取高亮字段的数据
     * @param hit
     * @return
     */
    private String getHighLightField(SearchHit hit) {
        Map<String, HighlightField> highlightFields = hit.getHighlightFields();
        if (!ObjectUtils.isEmpty(highlightFields)){
            HighlightField highlightField = highlightFields.get("name");
            Text[] fragments = highlightField.getFragments();
            if (!ObjectUtils.isEmpty(fragments)){
               return fragments[0].toString();
            }

        }
        return  null;
    }

    /**
     * 基础查询
     * @param requestParams
     * @param searchRequest
     */
    private void basicSearch(RequestParams requestParams, SearchRequest searchRequest) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //key 搜索框判断
        if (StringUtils.isNotEmpty(requestParams.getKey())){
            boolQueryBuilder.must(QueryBuilders.matchQuery("name",requestParams.getKey()));

        }else {
            boolQueryBuilder.must(QueryBuilders.matchAllQuery());
        }
        //brand 品牌判断
        if (StringUtils.isNotEmpty(requestParams.getBrand())) {
            boolQueryBuilder.must(QueryBuilders.termQuery("brand",requestParams.getBrand()));
        }

        //category 类别判断
        if (StringUtils.isNotEmpty(requestParams.getCategory())){
            boolQueryBuilder.must(QueryBuilders.termQuery("category",requestParams.getCategory()));
        }

        //price 价格判断
        if (Objects.nonNull(requestParams.getMaxPrice())){
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").lte(requestParams.getMaxPrice()).gte(requestParams.getMinPrice()));
        }



        //分数查询
        FunctionScoreQueryBuilder functionScoreQueryBuilder = new FunctionScoreQueryBuilder(
                QueryBuilders.functionScoreQuery(
                        boolQueryBuilder,new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                                new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery("isAD",true)
                                , ScoreFunctionBuilders.weightFactorFunction(10)
                                )
                        }
                ));

        //查询条件与request关联
        searchRequest.source().query(functionScoreQueryBuilder);

    }

    /**
     * 过滤项聚合功能
     *
     * @param requestParams
     * @return
     */
    @Override
    @SneakyThrows
    public Map<String, List<String>> aggregation(RequestParams requestParams) {
        SearchRequest searchRequest = new SearchRequest(INDEX_NAME);
        //调用基础查询函数
        basicSearch(requestParams, searchRequest);
        searchRequest.source().size(0);
        searchRequest.source().aggregation(AggregationBuilders.terms(AggregationEnum.CATEGORY.getAggregationName()).size(10).field(AggregationEnum.CATEGORY.getFieldName()));
        searchRequest.source().aggregation(AggregationBuilders.terms(AggregationEnum.BRAND.getAggregationName()).size(10).field(AggregationEnum.BRAND.getFieldName()));
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        ArrayList<String> categoryAgg = handleAggregation(searchResponse, AggregationEnum.CATEGORY.getAggregationName());
        ArrayList<String> brandAgg = handleAggregation(searchResponse, AggregationEnum.BRAND.getAggregationName());
        Map<String, List<String>> map = new HashMap<>(2);
        map.put(AggregationEnum.BRAND.getFieldName(), brandAgg);
        map.put(AggregationEnum.CATEGORY.getFieldName(), categoryAgg);
        return map;
    }

    private ArrayList<String> handleAggregation(SearchResponse searchResponse, String AggName) {
        ArrayList<String> list = new ArrayList<>();
        Aggregations aggregations = searchResponse.getAggregations();
        Terms terms = aggregations.get(AggName);
        List<? extends Terms.Bucket> buckets = terms.getBuckets();
        buckets.forEach(bucket -> {
            String key = bucket.getKeyAsString();
            list.add(key);
        });

        return list;
    }
}
